package com.piggy.logi.sys.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.piggy.common.core.exception.GlobalException;
import com.piggy.common.core.utils.PageUtils;
import com.piggy.common.core.utils.sql.SqlUtil;
import com.piggy.common.core.web.page.PagePlus;
import com.piggy.common.core.web.page.TableDataInfo;
import com.piggy.logi.sys.bo.LogiWaybillReceivableQueryBo;
import com.piggy.logi.sys.bo.LogiWaybillReceivableRecordEditBo;
import com.piggy.logi.sys.domain.*;
import com.piggy.logi.sys.enums.SettleStatusEnum;
import com.piggy.logi.sys.enums.SettleTypeEnum;
import com.piggy.logi.sys.enums.WaybillStatusEnum;
import com.piggy.logi.sys.mapper.LogiWaybillReceivableMapper;
import com.piggy.logi.sys.service.*;
import com.piggy.logi.sys.vo.LogiWaybillReceivableVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 应收管理Service业务层处理
 *
 * @author fengxy
 * @date 2023/10/17 21:54
 **/
@Slf4j
@Service
public class LogiWaybillReceivableServiceImpl extends ServiceImpl<LogiWaybillReceivableMapper, LogiWaybillReceivable> implements ILogiWaybillReceivableService {

    @Resource
    private ILogiWaybillService logiWaybillService;

    @Resource
    private ILogiWaybillSignService logiWaybillSignService;

    @Resource
    private ILogiWaybillFeeService logiWaybillFeeService;

    @Resource
    private ILogiCustomerService logiCustomerService;

    @Resource
    private ILogiWaybillReceivableRecordService logiWaybillPayableRecordService;

    @Resource
    private ILogiSysBaseConfigService logiSysBaseConfigService;

    @Override
    public TableDataInfo<LogiWaybillReceivableVo> queryPageList(LogiWaybillReceivableQueryBo bo) {
        PagePlus<LogiWaybillReceivable, LogiWaybillReceivableVo> pageVo = pageVo(PageUtils.buildPagePlus(bo, true), buildQueryWrapper(bo), LogiWaybillReceivableVo.class);
        pageVo.getRecordsVo().forEach(this::fillData);
        return PageUtils.buildDataInfo(pageVo);
    }

    @Override
    public LogiWaybillReceivableVo queryById(Long id) {
        LogiWaybillReceivableVo receivableVo = getVoById(id, LogiWaybillReceivableVo.class);
        if (ObjectUtil.isNull(receivableVo)) {
            throw new GlobalException("应收记录不存在");
        }
        fillData(receivableVo);
        return receivableVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void confirmPay(LogiWaybillReceivableRecordEditBo bo) {
        // 获取应收记录
        LogiWaybillReceivable receivable = getById(bo.getReceivableId());
        if (ObjectUtil.isNull(receivable)) {
            throw new GlobalException("应收记录不存在");
        }
        // 支付金额不能大于应收余额
        if (bo.getPayAmount().compareTo(receivable.getReceivableBalance()) > 0) {
            throw new GlobalException("支付金额不能大于应收余额");
        }
        // 新增收款记录
        LogiWaybillReceivableRecord record = BeanUtil.toBean(bo, LogiWaybillReceivableRecord.class);
        boolean success = logiWaybillPayableRecordService.save(record);
        if (!success) {
            throw new GlobalException("新增收款记录失败");
        }
        // 增加实收金额、减少应收余额、收款记录数量+1
        success = update(Wrappers.lambdaUpdate(LogiWaybillReceivable.class)
                .set(LogiWaybillReceivable::getRealAmount, receivable.getRealAmount().add(record.getPayAmount()))
                .set(LogiWaybillReceivable::getReceivableBalance, receivable.getReceivableBalance().subtract(record.getPayAmount()))
                .set(LogiWaybillReceivable::getRecordNum, receivable.getRecordNum() + 1)
                .eq(LogiWaybillReceivable::getId, receivable.getId()));
        if (!success) {
            throw new GlobalException("修改实收金额、应收余额、收款记录数量失败");
        }
    }

    @Override
    public List<LogiWaybillReceivableRecord> payList(Long id) {
        return logiWaybillPayableRecordService.list(Wrappers.lambdaQuery(LogiWaybillReceivableRecord.class)
                .eq(LogiWaybillReceivableRecord::getReceivableId, id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateRecord(LogiWaybillReceivableRecordEditBo bo) {
        // 校验参数
        if (ObjectUtil.isNull(bo.getId())) {
            throw new GlobalException("收款记录id为空");
        }
        // 查询老的收款记录
        LogiWaybillReceivableRecord oldRecord = logiWaybillPayableRecordService.getById(bo.getId());
        if (ObjectUtil.isNull(oldRecord)) {
            throw new GlobalException("收款记录不存在");
        }
        // 获取应收记录
        LogiWaybillReceivable receivable = getById(bo.getReceivableId());
        if (ObjectUtil.isNull(receivable)) {
            throw new GlobalException("应收记录不存在");
        }
        // 获取新的应收余额
        BigDecimal receivableBalance = receivable.getReceivableBalance().add(oldRecord.getPayAmount()).subtract(bo.getPayAmount());
        // 应收余额不能小于0
        if (receivableBalance.compareTo(BigDecimal.ZERO) < 0) {
            throw new GlobalException("应收余额不能小于0");
        }
        // 修改收款记录信息
        LogiWaybillReceivableRecord newRecord = BeanUtil.toBean(bo, LogiWaybillReceivableRecord.class);
        boolean success = logiWaybillPayableRecordService.updateById(newRecord);
        if (!success) {
            throw new GlobalException("修改收款记录信息失败");
        }
        // 更新实收金额、应收余额
        success = update(Wrappers.lambdaUpdate(LogiWaybillReceivable.class)
                .set(LogiWaybillReceivable::getRealAmount, receivable.getRealAmount().subtract(oldRecord.getPayAmount()).add(newRecord.getPayAmount()))
                .set(LogiWaybillReceivable::getReceivableBalance, receivableBalance)
                .eq(LogiWaybillReceivable::getId, receivable.getId()));
        if (!success) {
            throw new GlobalException("修改实收金额、应收余额失败");
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRecord(Long id) {
        // 查询收款记录
        LogiWaybillReceivableRecord record = logiWaybillPayableRecordService.getById(id);
        if (ObjectUtil.isNull(record)) {
            throw new GlobalException("收款记录不存在");
        }
        // 获取应收记录
        LogiWaybillReceivable receivable = getById(record.getReceivableId());
        if (ObjectUtil.isNull(receivable)) {
            throw new GlobalException("应收记录不存在");
        }
        // 删除收款记录
        boolean success = logiWaybillPayableRecordService.removeById(id);
        if (!success) {
            throw new GlobalException("删除收款记录失败");
        }
        // 减少实收金额、增加应收余额、收款记录数量-1
        success = update(Wrappers.lambdaUpdate(LogiWaybillReceivable.class)
                .set(LogiWaybillReceivable::getRealAmount, receivable.getRealAmount().subtract(record.getPayAmount()))
                .set(LogiWaybillReceivable::getReceivableBalance, receivable.getReceivableBalance().add(record.getPayAmount()))
                .set(LogiWaybillReceivable::getRecordNum, receivable.getRecordNum() - 1)
                .eq(LogiWaybillReceivable::getId, receivable.getId()));
        if (!success) {
            throw new GlobalException("修改实收金额、应收余额、收款记录数量失败");
        }
    }

    @Override
    public void confirmSettle(Long id) {
        // 查询应收信息
        LogiWaybillReceivable receivable = getById(id);
        if (ObjectUtil.isNull(receivable)) {
            throw new GlobalException("应收记录不存在");
        }
        // 校验状态是否为未结清
        if (SettleStatusEnum.NOT_SETTLE.getCode() != receivable.getStatus()) {
            throw new GlobalException("请选择未结清的应收记录");
        }
        // 校验应收余额是否为0
        LogiSysBaseConfig sysBaseConfig = logiSysBaseConfigService.getBaseConfig(receivable.getDeptId());
        if (sysBaseConfig.getReceivableSettled() == 1 && BigDecimal.ZERO.compareTo(receivable.getReceivableBalance()) != 0) {
            throw new GlobalException("应收余额不为0");
        }
        // 修改状态未：已结清
        boolean success = update(Wrappers.lambdaUpdate(LogiWaybillReceivable.class)
                .set(LogiWaybillReceivable::getStatus, SettleStatusEnum.SETTLED.getCode())
                .eq(LogiWaybillReceivable::getId, receivable.getId()));
        if (!success) {
            throw new GlobalException("修改结清状态状态已结清失败");
        }
    }

    @Override
    public void cancelSettle(Long id) {
        // 查询应收信息
        LogiWaybillReceivable receivable = getById(id);
        if (ObjectUtil.isNull(receivable)) {
            throw new GlobalException("应收记录不存在");
        }
        // 判断状态是否为已结清
        if (SettleStatusEnum.SETTLED.getCode() != receivable.getStatus()) {
            throw new GlobalException("请选择已结清的应收记录");
        }
        // 修改状态未：未结清
        boolean success = update(Wrappers.lambdaUpdate(LogiWaybillReceivable.class)
                .set(LogiWaybillReceivable::getStatus, SettleStatusEnum.NOT_SETTLE.getCode())
                .eq(LogiWaybillReceivable::getId, receivable.getId()));
        if (!success) {
            throw new GlobalException("修改结清状态状态未结清失败");
        }
    }

    @Override
    public void removeByUK(Long deptId, Long waybillId) {
        baseMapper.removeByUK(deptId, waybillId);
    }

    /**
     * 组装查询条件
     **/
    private Wrapper<LogiWaybillReceivable> buildQueryWrapper(LogiWaybillReceivableQueryBo bo) {
        LambdaQueryWrapper<LogiWaybillReceivable> lqw = Wrappers.lambdaQuery();
        lqw.like(bo.getOutCode() != null, LogiWaybillReceivable::getOutCode, bo.getOutCode());
        lqw.eq(bo.getSettlementMode() != null, LogiWaybillReceivable::getSettlementMode, bo.getSettlementMode());
        lqw.eq(bo.getStatus() != null, LogiWaybillReceivable::getStatus, bo.getStatus());
        lqw.between(ObjectUtil.isAllNotEmpty(bo.getCreateStartDate(), bo.getCreateEndDate()), LogiWaybillReceivable::getCreateTime, bo.getCreateStartDate(), bo.getCreateEndDate());
        // 客户名称筛选
        if (StrUtil.isNotBlank(bo.getCustomerName())) {
            String existsSql = "select 1 from logi_customer where logi_customer.customer_id = logi_waybill_receivable.payer_id" +
                    " and logi_customer.customer_name like '%" + bo.getCustomerName() + "%'";
            lqw.exists(existsSql);
        }
        SqlUtil.dataPermissionFilter(lqw, bo.getDeptId());
        return lqw;
    }

    /**
     * 补充字段数据
     **/
    private void fillData(LogiWaybillReceivableVo receivableVo) {
        // 设置收发货人名称
        List<LogiCustomer> customers = logiCustomerService.list(Wrappers.lambdaQuery(LogiCustomer.class)
                .in(LogiCustomer::getCustomerId, CollUtil.newArrayList(receivableVo.getShipId(), receivableVo.getDeliveryId())));
        Map<Long, String> customerMap = customers.stream().collect(Collectors.toMap(LogiCustomer::getCustomerId, LogiCustomer::getCustomerName));
        receivableVo.setShipName(customerMap.getOrDefault(receivableVo.getShipId(), ""));
        receivableVo.setDeliveryName(customerMap.getOrDefault(receivableVo.getDeliveryId(), ""));
        // 设置运单状态
        LogiWaybill waybill = logiWaybillService.getById(receivableVo.getWaybillId());
        receivableVo.setWaybillStatus(waybill.getStatus());
        // 设置剩余时间（只有月结类型且运单状态为已签收之后的状态才有值）
        if (ObjectUtil.isNotNull(receivableVo.getSettlementMode()) && receivableVo.getSettlementMode() == SettleTypeEnum.ms.getCode()
                && receivableVo.getWaybillStatus() >= WaybillStatusEnum.SIGNATURE.getCode()) {
            // 获取签收时间
            LogiWaybillSign waybillSign = logiWaybillSignService.getOne(Wrappers.lambdaQuery(LogiWaybillSign.class)
                    .eq(LogiWaybillSign::getWaybillId, receivableVo.getWaybillId()));
            // 获取月结天数
            LogiWaybillFee waybillFee = logiWaybillFeeService.getOne(Wrappers.lambdaQuery(LogiWaybillFee.class)
                    .eq(LogiWaybillFee::getWaybillId, receivableVo.getWaybillId()));
            // 计算剩余时间
            if (ObjectUtil.isNotNull(waybillSign.getUpdateTime()) && ObjectUtil.isNotNull(waybillFee.getMsDay())) {
                receivableVo.setRemainDay(Math.toIntExact(waybillFee.getMsDay() - DateUtil.betweenDay(waybillSign.getUpdateTime(), DateUtil.date(), true)));
            }
        }
    }
}
